import { Application, Context } from "egg";
import { ApolloServer, AuthenticationError, toApolloError } from "apollo-server-koa";
import compose from "koa-compose";
import schema from "../graphql";
import { Admin } from "@prisma/client";
import moment from "moment";

const isDev = process.env.NODE_ENV !== "production";

export default (_, app: Application) => {
  const graphqlConfig = app.config.graphql;

  const options = { ...graphqlConfig };

  const { graphiql = true, router, ...ApolloServerConfig } = options;

  const server = new ApolloServer({
    schema,
    context: async (options) => {
      const { ctx } = options as { ctx: Context };
      try {
        const { authorization } = ctx.req.headers;
        let token: any = null;
        let admin: Admin | null = null;

        if (authorization) {
          token = ctx.service.encryption.decodeToken(authorization as string);

          const {
            sub: { adminId, wechatAccountId, userId },
          } = token;
          if (adminId) {
            admin = await ctx.db.admin.findUnique({ where: { id: adminId }, rejectOnNotFound: true });
          }

          ctx.token = token;
          ctx.admin = admin;
          ctx.adminId = adminId;
        }

        return ctx;
      } catch (error) {
        throw new AuthenticationError("登录已过期，请重新登录");
      }
    },
    debug: isDev,
    formatError: (error) => {
      if (isDev) {
        console.error("error", moment().format(), JSON.stringify(error, null, 2));
      }

      switch (error.name) {
        case "ValidationError": {
          return toApolloError(error);
        }
        case "GraphQLError": {
          switch (error.originalError?.name) {
            case "NotFoundError": {
              error.message = "记录不存在";
              return toApolloError(error);
            }
            case "AuthenticationError": {
              return toApolloError(error, "AuthenticationError");
            }
            case "TypeError":
            case "Error": {
              console.error("error", moment().format(), JSON.stringify(error, null, 2));
              error.message = "Internal server error";
              return toApolloError(error);
            }
            default: {
              return toApolloError(error);
            }
          }
        }
        default: {
          console.error("error", JSON.stringify(error, null, 2));
          error.message = "Internal server error";
          return toApolloError(error);
        }
      }
    },
    ...ApolloServerConfig,
  });

  // app.once("server", (httpServer) => {
  //   server.installSubscriptionHandlers(httpServer);
  // });

  app.GraphqlServer = server;

  const middlewares: any[] = [];

  const proxyApp = {
    use: (m: any) => {
      middlewares.push(m);
    },
  };
  // 启用KOA-APOLLO
  server.applyMiddleware({
    app: proxyApp as any,
    path: router,
  });

  return compose(middlewares);
};
